import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
  ActivityIndicator,
  Dimensions,
  findNodeHandle,
  LayoutAnimation,
  Platform,
  RefreshControl,
  ScrollView,
  StyleSheet,
  Text,
  TouchableWithoutFeedback,
  View,
  VirtualizedList,
} from 'react-native';
import FakeScrollView from './FakeScrollView';
import RefreshableScrollView from '../UltimateListView/refreshableScrollView';


let {width, height} = Dimensions.get('window');
const PaginationStatus = {
  firstLoad: 0,
  waiting: 1,
  loading: 2,
  loadFailed: 3,
  allLoaded: 4,
};

const _stateFromProps = ({numColumns, getHeightForItem}) => {
  const columns = Array.from({
    length: numColumns,
  }).map((col, i) => ({
    index: i,
    totalHeight: 0,
    data: [],
    heights: [],
  }));

  return {columns};
};

export default class WaterfallListView extends Component {
  static defaultProps = {
    initialNumToRender: 10,
    horizontal: false,
    headerCount: 0,
    headerAtIndex: null,
    firstLoader: true,
    scrollEnabled: true,
    onFetch: null,
    enableEmptySections: true,

    // Custom View
    header: null,
    item: null,
    paginationAllLoadedView: null,
    paginationWaitingView: null,

    // Refreshable
    refreshable: true,
    refreshableMode: 'basic', // basic or advanced

    // RefreshControl
    refreshableTitle: null,
    refreshableColors: ['dimgray', 'tomato', 'limegreen'],
    refreshableProgressBackgroundColor: 'white',
    refreshableSize: undefined,
    refreshableTintColor: 'lightgray',
    customRefreshControl: null,

    // Advanced RefreshView
    refreshableTitlePull: '下拉可以刷新',
    refreshableTitleRefreshing: '正在刷新数据中',
    refreshableTitleRelease: '松开立即刷新',
    customRefreshView: null,
    displayDate: false,
    dateFormat: 'yyyy-MM-dd hh:mm',
    dateTitle: '最后更新: ',
    arrowImageStyle: undefined,
    refreshViewStyle: undefined,
    dateStyle: undefined,
    refreshViewHeight: 80,

    // Pagination
    pagination: true,
    autoPagination: true,
    allLoadedText: '已经全部加载完毕',

    // Spinner
    spinnerColor: undefined,
    fetchingSpinnerSize: 'large',
    waitingSpinnerSize: 'small',
    waitingSpinnerText: '加载中...',
    pageErrorText: '加载失败, 点击重新加载',
    // Pagination Button
    paginationBtnText: '加载更多中...',

    //state text
    emptyViewText: '无数据',
    loadingErrorText: '加载失败, 点击重新加载',
    // GridView
    numColumns: 1,
    stickyHeaderIndex: -1,
    startPage: 1,
  };

  static propTypes = {
    initialNumToRender: PropTypes.number,
    horizontal: PropTypes.bool,

    firstLoader: PropTypes.bool,
    scrollEnabled: PropTypes.bool,
    onFetch: PropTypes.func,
    enableEmptySections: PropTypes.bool,

    // Custom ListView
    header: PropTypes.func,
    item: PropTypes.func,
    paginationAllLoadedView: PropTypes.func,
    paginationWaitingView: PropTypes.func,
//    emptyView: PropTypes.func,
    stateView: PropTypes.func,
    // Refreshable
    refreshable: PropTypes.bool,
    refreshableMode: PropTypes.string,

    // RefreshControl
    refreshableTitle: PropTypes.string,
    refreshableColors: PropTypes.array,
    refreshableProgressBackgroundColor: PropTypes.string,
    refreshableSize: PropTypes.string,
    refreshableTintColor: PropTypes.string,
    customRefreshControl: PropTypes.func,

    // Advanced RefreshView
    refreshableTitlePull: PropTypes.string,
    refreshableTitleRefreshing: PropTypes.string,
    refreshableTitleRelease: PropTypes.string,
    customRefreshView: PropTypes.func,
    displayDate: PropTypes.bool,
    dateFormat: PropTypes.string,
    dateTitle: PropTypes.string,
    arrowImageStyle: PropTypes.object,
    refreshViewStyle: PropTypes.object,
    dateStyle: PropTypes.object,
    refreshViewHeight: PropTypes.number,


    // Pagination
    pagination: PropTypes.bool,
    autoPagination: PropTypes.bool,
    allLoadedText: PropTypes.string,

    // Spinner
    spinnerColor: PropTypes.string,
    fetchingSpinnerSize: PropTypes.any,
    waitingSpinnerSize: PropTypes.any,
    waitingSpinnerText: PropTypes.string,
    pageErrorText: PropTypes.string,
    // Pagination Button
    paginationBtnText: PropTypes.string,

    //stateText
    loadingErrorText: PropTypes.string,
    emptyViewText: PropTypes.string,
    // GridView
    numColumns: PropTypes.number,
    /**
     * How to render a row in a FlatList. Should return a valid React Element.
     */
    renderItem: PropTypes.func,

    stickyHeaderIndex: PropTypes.number,

    startPage: PropTypes.number,
    footer: PropTypes.func,
    getHeightForItem: PropTypes.func,
  };

  constructor(props) {
    super(props);
    this.setPage(props.startPage);
    this.setRows([]);

    //swipe
    this._rows = {};
    this.headerHeight = 0;
    if (Platform.OS === 'ios') {
      // Keep track of scroll offset and layout changes on iOS to be able to handle
      // https://github.com/jemise111/react-native-swipe-list-view/issues/109
    }

    const state = _stateFromProps(this.props);
    this._listRefs = [];
    this.state = {
      ...state,
      dataSource: [],
      isRefreshing: false,
      paginationStatus: PaginationStatus.firstLoad,
      error: '',
      listHeight: (height - 64),
    };
  }

  onRefresh = () => {
    console.log('onRefresh()');
    if (this.mounted) {
      this.setState({
        isRefreshing: true,
      });
      this.setPage(this.props.startPage);
      this.props.onFetch(this.getPage(), this.postRefresh, this.abortFetch);
    }
  };

  onPaginate = () => {
    if (this.state.paginationStatus !== PaginationStatus.allLoaded && this.state.paginationStatus !== PaginationStatus.loading && !this.state.isRefreshing) {
      console.log('onPaginate()');
      const rows = this.rows || [];
      this.setState({paginationStatus: PaginationStatus.loading, dataSource: rows.slice()}, () => {
        this.calculateColumnHeight(this.state.columns);
        this.forceUpdate();
      });
      this.props.onFetch(this.getPage() + 1, this.postPaginate, this.abortFetch);
    }
  };

  onEndReached = () => {
    // console.log('onEndReached()');
    if (this.props.pagination && this.props.autoPagination && this.state.paginationStatus === PaginationStatus.waiting) {
      this.onPaginate();
    }
  };

  setPage = page => this.page = page;

  getPage = () => this.page;

  setRows = rows => this.rows = rows;

  getRows = () => this.rows;
  refresh = () => {
    this.onRefresh();
  };

  scrollToIndex = (option) => {
    this._scrollRef.scrollToIndex(option);
  };

  scrollToItem = (option) => {
    this._scrollRef.scrollToItem(option);
  };

  scrollToEnd = (option) => {
    this._scrollRef.scrollToEnd(option);
  };

  postRefresh = (rows = [], pageLimit) => {
    if (this.mounted) {
      let paginationStatus = PaginationStatus.waiting;
      if (rows.length < pageLimit) {
        paginationStatus = PaginationStatus.allLoaded;
      }
      this.updateRows(rows, paginationStatus, '');
    }
  };

  endFetch = () => {
    console.log('endFetch()');
    if (this.mounted) {
      if (this.props.refreshableMode === 'advanced') {
        this._scrollRef.onRefreshEnd && this._scrollRef.onRefreshEnd();
      }
    }
  };

  abortFetch = (error) => {
    this.updateRows(null, PaginationStatus.loadFailed, error);
  };

  postPaginate = (rows = [], pageLimit) => {
    this.setPage(this.getPage() + 1);
    let paginationStatus;
    const oldrows = this.rows || [];
    let mergedRows = oldrows.concat(rows);
    if (rows.length === 0 || rows.length < pageLimit) {
      paginationStatus = PaginationStatus.allLoaded;
    } else {
      paginationStatus = PaginationStatus.waiting;
    }

    this.updateRows(mergedRows, paginationStatus, '');
  };

  updateRows = (rows, paginationStatus, error) => {
    if (rows) {
      this.setRows(rows);
      this.setState({
        dataSource: rows,
        isRefreshing: false,
        paginationStatus,
        error,
      }, () => {
        this.calculateColumnHeight(this.state.columns);
        this.forceUpdate();
      });
    } else {
      const oldrows = this.rows || [];
      this.setState({
        dataSource: oldrows.slice(),
        isRefreshing: false,
        paginationStatus,
        error,
      }, () => {
        this.calculateColumnHeight(this.state.columns);
        this.forceUpdate();
      });
    }

    if (this.props.refreshableMode === 'advanced') {
      this.endFetch();
    }
  };

  paginationAllLoadedView = () => {
    if (this.props.pagination) {
      if (this.props.paginationAllLoadedView) {
        return this.props.paginationAllLoadedView();
      }

      return (
        <View style={styles.paginationView}>
          <Text style={styles.allLoadedText}>
            {this.props.allLoadedText}
          </Text>
        </View>
      );
    }
    return this.props.footer && this.props.footer();
  };

  paginationErrorView = (paginateCallback) => {
    if (this.props.pagination) {
      if (this.props.autoPagination) {
        return (
          <TouchableWithoutFeedback onPress={() => paginateCallback()}>
            <View style={styles.paginationView}>
              <Text
                style={[styles.paginationViewText, {marginLeft: 5}]}
              >{this.props.pageErrorText}
              </Text>
            </View>
          </TouchableWithoutFeedback>
        );
      }
    }

    return this.props.footer && this.props.footer();
  };

  paginationWaitingView = (paginateCallback) => {
    if (this.props.pagination) {
      if (this.props.autoPagination) {
        if (this.props.paginationWaitingView) {
          return this.props.paginationWaitingView(paginateCallback);
        }

        return (
          <View style={styles.paginationView}>
            <ActivityIndicator color={this.props.spinnerColor} size={this.props.waitingSpinnerSize}/>
            <Text
              style={[styles.paginationViewText, {marginLeft: 5}]}
            >{this.props.waitingSpinnerText}
            </Text>
          </View>
        );
      }
    }

    return this.props.footer && this.props.footer();
  };

  renderHeader = () => {
    if (this.props.header) {
      return <View onLayout={event => {
        this.headerHeight = event.nativeEvent.layout.height;
      }}>{this.props.header()}</View>;
    }

    return null;
  };

  renderStateView = () => {
    //刚进入
    if (this.state.paginationStatus === PaginationStatus.firstLoad) {
      if (this.props.stateView) {
        return this.props.stateView(0);
      } else {
        return <View style={{
          justifyContent: 'center',
          alignItems: 'center',
          flexDirection: 'row',
          flex: 1,
          height: this.state.listHeight,
        }}>
          <ActivityIndicator size={'small'}/>
          <Text style={{
            fontWeight: '400',
            fontSize: 20,
            color: '#666666',
            marginLeft: 10,
          }}>{this.props.waitingSpinnerText}</Text>
        </View>;
      }
    }
    //第一次加载失败
    else if (this.state.paginationStatus === PaginationStatus.loadFailed) {
      if (this.props.stateView) {
        return this.props.stateView(2, this.state.error, this.refresh);
      } else {
        return <TouchableWithoutFeedback onPress={() => {
          this.setState({paginationStatus: PaginationStatus.firstLoad});
          //this.refresh();
          this.props.onFetch(this.getPage(), this.postRefresh, this.abortFetch);
        }}><View style={{
          justifyContent: 'center',
          alignItems: 'center',
          flex: 1,
          height: this.state.listHeight,
        }}><Text style={{
          fontWeight: '400',
          fontSize: 20,
          color: '#666666',
        }}>{this.state.error}</Text></View></TouchableWithoutFeedback>;
      }
    }
    //第一次加载无数据
    else if (this.state.paginationStatus !== PaginationStatus.firstLoad) {
      if (this.props.stateView) {
        return this.props.stateView(1);
      } else {
        return <View style={{
          alignItems: 'center',
          justifyContent: 'center',
          flexGrow: 1,
          height: Math.max(this.state.listHeight - this.headerHeight, this.state.listHeight / 2),
        }}>
          <Text style={{
            fontWeight: '400',
            fontSize: 20,
            color: '#666666',
          }}>{this.props.emptyViewText}</Text>
        </View>;
      }
    }

    return null;
  };

  renderFooter = () => {
    //有数据, 但是加载失败了
    if (this.props.pagination) {
      if (this.state.paginationStatus === PaginationStatus.loadFailed && this.rows && this.rows.length !== 0) {
        return this.paginationErrorView(this.onPaginate);
      } else if (this.state.paginationStatus === PaginationStatus.waiting && this.props.autoPagination === false) {
        return this.paginationWaitingView(this.onPaginate);
      } else if (this.state.paginationStatus === PaginationStatus.waiting && this.props.autoPagination === true) {
        return this.paginationWaitingView();
      } else if (this.state.paginationStatus === PaginationStatus.loading) {
        return this.paginationWaitingView();
      } else if (this.rows && this.rows.length !== 0 && this.state.paginationStatus === PaginationStatus.allLoaded) {
        return this.paginationAllLoadedView();
      }
    }
    if (this.props.footer) {
      return this.props.footer && this.props.footer();
    }
    return null;
  };

  // renderScrollComponent2 = (props) => {
  //   if (props.onRefresh && props.refreshing != null) {
  //     return (
  //       <ScrollView
  //         {...props}
  //         automaticallyAdjustContentInsets={false}
  //         refreshControl={
  //           <RefreshControl
  //             refreshing={props.refreshing}
  //             onRefresh={props.onRefresh}
  //           />
  //         }
  //       />
  //     );
  //   }
  //   return <ScrollView {...props} />;
  // };

  deleteRow = (item) => {
    let dataSource = this.state.dataSource.slice();
    dataSource.splice(item.index, 1);
    LayoutAnimation.easeInEaseOut();
  };

  _keyExtractor = (item, index) => {
    if (typeof (item) === 'object') {
      return item.key !== undefined ? item.key : `${index}`;
    } else {
      return `${index}`;
    }
  };

  componentDidMount() {
    this.mounted = true;
    if (this.props.firstLoader) {
      this.props.onFetch(this.getPage(), this.postRefresh, this.abortFetch);
    }
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  updateDataSource(rows = []) {
    this.setRows(rows);
    this.setState({
      dataSource: rows,
    }, () => {
      this.calculateColumnHeight(this.state.columns);
      this.forceUpdate();
    });

  }

  calculateColumnHeight(columns) {
    this.state.columns.forEach(column => {
      column.data = [];
      column.heights = [];
      column.totalHeight = 0;
    });
    this.state.dataSource.forEach((item, index) => {

      const height = this.props.getHeightForItem({item, index});
      console.log('height:', height);
      const column = this.state.columns.reduce(
        (prev, cur) => (cur.totalHeight < prev.totalHeight ? cur : prev),
        columns[0],
      );
      column.data.push(item);
      column.heights.push(height);
      column.totalHeight += height;
    });
  }

  render() {

    const ListHeaderComponent = this.renderHeader();
    const ListEmptyComponent = this.renderStateView();
    const ListFooterComponent = this.renderFooter();
    const {
      renderItem,
      ...props
    } = this.props;

    const that = this;
    const content = (
      <View style={styles.contentContainer}>
        {this.state.columns.map((col, index) =>
          <VirtualizedList
            {...props}
            ref={ref => (that._listRefs[col.index] = ref)}
            key={`$col_${col.index}`}
            data={col.data}
            getItemCount={this._getItemCount}
            getItem={this._getItem}
            getItemLayout={(data, index) =>
              this._getItemLayout(col.index, index)}
            renderItem={({item, index}) =>
              renderItem({item, index, column: col.index})}
            renderScrollComponent={this._renderScrollComponent}
            keyExtractor={this._keyExtractor}
            onEndReached={this.onEndReached}
            onEndReachedThreshold={this.props.onEndReachedThreshold}
            removeClippedSubviews={false}
          />,
        )}
      </View>
    );

    if (Platform.OS === 'ios') {
      return <RefreshableScrollView
        {...props}
        insideOfUltimateListView
        onRefresh={this.onRefresh}
        scrollEventThrottle={50}
        removeClippedSubviews={false}
        onContentSizeChange={this._onContentSizeChange}
        onLayout={this._onLayout}
        onScroll={this._onScroll}
        onScrollBeginDrag={this._onScrollBeginDrag}
        onScrollEndDrag={this._onScrollEndDrag}
        onMomentumScrollEnd={this._onMomentumScrollEnd}
        ref={this._captureScrollRef}
        // refreshControl={<RefreshControl
        //   onRefresh={this.onRefresh}
        //   refreshing={this.state.isRefreshing}
        // />}
      >
        {ListHeaderComponent}
        {ListEmptyComponent && this.state.dataSource.length === 0 ? ListEmptyComponent : content}
        {this.state.dataSource.length === 0 ? null : ListFooterComponent}
      </RefreshableScrollView>;
    } else {
      return <ScrollView
        {...props}
        onLayout={event => {
          this.setState({listHeight: event.nativeEvent.layout.height});
        }}
        refreshControl={<RefreshControl
          onRefresh={this.onRefresh}
          style={{zIndex: 10000}}
          refreshing={this.state.isRefreshing}
          colors={this.props.refreshableColors}
          progressBackgroundColor={this.props.refreshableProgressBackgroundColor}
          size={this.props.refreshableSize}
          tintColor={this.props.refreshableTintColor}
          title={this.props.refreshableTitle}
          progressViewOffset={this.props.contentContainerStyle ? this.props.contentContainerStyle.paddingTop : 0}
        />}
        scrollEventThrottle={50}
        removeClippedSubviews={false}
        onContentSizeChange={this._onContentSizeChange}
        onLayout={this._onLayout}
        onScroll={this._onScroll}
        onScrollBeginDrag={this._onScrollBeginDrag}
        onScrollEndDrag={this._onScrollEndDrag}
        onMomentumScrollEnd={this._onMomentumScrollEnd}
        ref={this._captureScrollRef}
      >
        {ListHeaderComponent}
        {ListEmptyComponent && this.state.dataSource.length === 0 ? ListEmptyComponent : content}
        {this.state.dataSource.length === 0 ? null : ListFooterComponent}
      </ScrollView>;
    }
  }

  getScrollResponder() {
    if (this._scrollRef && this._scrollRef.getScrollResponder) {
      return this._scrollRef.getScrollResponder();
    }
    return null;
  }

  getScrollableNode() {
    if (this._scrollRef && this._scrollRef.getScrollableNode) {
      return this._scrollRef.getScrollableNode();
    }
    return findNodeHandle(this._scrollRef);
  }

  scrollToOffset({offset, animated}: any) {
    if (this._scrollRef) {
      this._scrollRef.scrollTo({y: offset, animated});
    }
  }

  _onLayout = event => {
    this.setState({listHeight:event.nativeEvent.layout.height}, ()=>{
      this._listRefs.forEach(
        list => list && list._onLayout && list._onLayout(event),
      );
    });
  };

  _onContentSizeChange = (width, height) => {
    this._listRefs.forEach(
      list =>
        list &&
        list._onContentSizeChange &&
        list._onContentSizeChange(width, height),
    );
  };

  _onScroll = event => {
    if (this.props.onScroll) {
      this.props.onScroll(event);
    }
    this._listRefs.forEach(
      list => list && list._onScroll && list._onScroll(event),
    );
  };

  _onScrollBeginDrag = event => {
    if (this.props.onScrollBeginDrag) {
      this.props.onScrollBeginDrag(event);
    }
    this._listRefs.forEach(
      list => list && list._onScrollBeginDrag && list._onScrollBeginDrag(event),
    );
  };

  _onScrollEndDrag = event => {
    if (this.props.onScrollEndDrag) {
      this.props.onScrollEndDrag(event);
    }
    this._listRefs.forEach(
      list => list && list._onScrollEndDrag && list._onScrollEndDrag(event),
    );
  };

  _onMomentumScrollEnd = event => {
    if (this.props.onMomentumScrollEnd) {
      this.props.onMomentumScrollEnd(event);
    }
    this._listRefs.forEach(
      list =>
        list && list._onMomentumScrollEnd && list._onMomentumScrollEnd(event),
    );
  };

  _getItemLayout = (columnIndex, rowIndex) => {
    const column = this.state.columns[columnIndex];
    let offset = 0;
    for (let ii = 0; ii < rowIndex; ii += 1) {
      offset += column.heights[ii];
    }
    return {length: column.heights[rowIndex], offset, index: rowIndex};
  };

  _renderScrollComponent = () => <FakeScrollView style={styles.column}/>;

  _getItemCount = data => data.length;

  _getItem = (data, index) => data[index];

  _captureScrollRef = ref => (this._scrollRef = ref);
}

const styles = StyleSheet.create({
  fetchingView: {
    width,
    height,
    justifyContent: 'center',
    alignItems: 'center',
  },
  paginationView: {
    flex: 0,
    width,
    height: 55,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
  },
  paginationViewText: {
    fontSize: 16,
  },
  paginationViewSpinner: {
    marginRight: 5,
  },
  paginationBtn: {
    backgroundColor: 'tomato',
    margin: 10,
    borderRadius: 20,
    flex: 1,
    height: 50,
    justifyContent: 'center',
    alignItems: 'center',
  },
  paginationBtnText: {
    fontSize: 16,
    color: 'white',
    fontWeight: 'bold',
  },
  emptyView: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  allLoadedText: {
    alignSelf: 'center',
    color: '#bfbfbf',
  },
  contentContainer: {
    flexDirection: 'row',
  },
  column: {
    flex: 1,
  },
});
